// <copyright file="CavernGenerator.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>April 2009</date>
// <summary>Cavern style level generator module.</summary>

// License:

// Product: RPGRoguelikeEngine .Net
// Author: Robert N. Tenney
// Copyright: April 2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Collections.Generic;
using System.Drawing;
using TenneySoftware.Engine;
using TenneySoftware.Engine.Algorithms;

namespace TenneySoftware.LevelGenerator.CavernGenerator {
    /// <summary>
    /// Cavern Generation alorithm that uses the cellular automata algorithm for generation
    /// of realistic caverns and tunnels which allows many different customizing options.
    /// </summary>
    public partial class Cavern : ILevelGenerator {
        // TODO: Add analyzer to determine if a section is a cavern or tunnel for better item
        // placement, as well as connecting disconnected sections.

        /// <summary>
        /// Level object representing the generated level.
        /// </summary>
        private Level level;

        /// <summary>
        /// Array of grid types that represents the cellular automata generated pattern.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Member", Justification = "Multidimensional preferred")]
        private GridType[,] grid;

        /// <summary>
        /// A list of passages that lead out of the level.
        /// </summary>
        private SkipListCollection passageOut;

        /// <summary>
        /// The name of the settings being used by this instance.
        /// </summary>
        private string settingName;

        /// <summary>
        /// The cellular automata object for use with the CA pattern generation.
        /// </summary>
        private CellularAutomata cellAutomata;

        /// <summary>
        /// Initializes a new instance of the Cavern class.
        /// </summary>
        public Cavern() :
        this(0, new Size(64, 64)) {
        }

        /// <summary>
        /// Initializes a new instance of the Cavern class.
        /// </summary>
        /// <param name="size">Size to set level to.</param>
        public Cavern(Size size) :
        this(0, size) {
        }

        /// <summary>
        /// Initializes a new instance of the Cavern class.
        /// </summary>
        /// <param name="predefined">PredefinedSettings integer to use.</param>
        public Cavern(int predefined) :
        this(predefined, new Size(64, 64)) {
        }

        /// <summary>
        /// Initializes a new instance of the Cavern class.
        /// </summary>
        /// <param name="predefined">PredefinedSettings integer to use.</param>
        /// <param name="size">Size to set level to.</param>
        public Cavern(int predefined, Size size) {
            this.cellAutomata = new CellularAutomata();
            this.UsePredefinedSettings(predefined);
            this.SetSize(size);
            this.passageOut = new SkipListCollection();
        }

        /// <summary>
        /// Returns a string presention of the Cavern Generator.
        /// </summary>
        /// <returns>A string represenation of this class.</returns>
        public override string ToString() {
            return this.ModuleName + " | " + this.SettingsName;
        }

        /// <summary>
        /// Class to help in controlling(seeding) exit points for cavern.
        /// </summary>
        private class PassageOutSeed {
            /// <summary>
            /// Point that represents the top of the grid.
            /// </summary>
            private Point top = new Point(0, -1);

            /// <summary>
            /// Point that represents the center of the grid.
            /// </summary>
            private Point center = new Point(0, 0);

            /// <summary>
            /// Point that represents the bottom of the grid.
            /// </summary>
            private Point bottom = new Point(0, 1);

            /// <summary>
            /// Point that represents the left of the grid.
            /// </summary>
            private Point left = new Point(-1, 0);

            /// <summary>
            /// Point that represents the right of the grid.
            /// </summary>
            private Point right = new Point(1, 0);

            /// <summary>
            /// Initializes a new instance of the PassageOutSeed class.
            /// </summary>
            public PassageOutSeed() {
            }

            /// <summary>
            /// Initializes a new instance of the PassageOutSeed class.
            /// </summary>
            /// <param name="origin">Point for exit connection.</param>
            public PassageOutSeed(Point origin) {
                // Default offsets....
                this.top.Offset(origin);
                this.center.Offset(origin);
                this.bottom.Offset(origin);
                this.left.Offset(origin);
                this.right.Offset(origin);
            }

            /// <summary>
            /// Gets the top point of the five point grid.
            /// </summary>
            public Point Top {
                get {
                    return this.top;
                }
            }

            /// <summary>
            /// Gets the center point of the five point grid.
            /// </summary>
            public Point Center {
                get {
                    return this.center;
                }
            }

            /// <summary>
            /// Gets the bottom point of the five point grid.
            /// </summary>
            public Point Bottom {
                get {
                    return this.bottom;
                }
            }

            /// <summary>
            /// Gets the left point of the five point grid.
            /// </summary>
            public Point Left {
                get {
                    return this.left;
                }
            }

            /// <summary>
            /// Gets the right point of the five point grid.
            /// </summary>
            public Point Right {
                get {
                    return this.right;
                }
            }

            /// <summary>
            /// Shifts the exit point to the appropriate side of the level.
            /// </summary>
            /// <param name="location">Location to shift.</param>
            /// <param name="size">Size of the level.</param>
            /// <returns>The shifted point.</returns>
            public static Point Shift(Point location, Size size) {
                if (location.X == -1) {
                    location.Offset(2, 0);
                }

                if (location.Y == -1) {
                    location.Offset(0, 2);
                }

                if (location.X == size.Width) {
                    location.Offset(-2, 0);
                }

                if (location.Y == size.Height) {
                    location.Offset(0, -2);
                }

                return location;
            }
        }
    }
}
